home *** CD-ROM | disk | FTP | other *** search
- page 60,132
- title PAINT
-
- public paint
-
- ; Routine to fill an area
- ; Ref: Christopher Morgan, Bluebook of Assembly Routines for the
- ; IBM PC & XT, Plume/Waite (1984), pp. 156-160
- ;
- ; Callable from Pascal as
- ;
- ; PROCEDURE PAINT (x,y,color : integer);
- ;
- ; This routine fills an area on the graphics screen with a specified
- ; color. It starts "painting" at a specified interior position filling
- ; a region bounded by a "boundary" color.
- ;
- ; The region must be completely surrounded by a boundary drawn in the
- ; boundary color. Any paint color in the region can obstruct the
- ; filling process, acting just like a boundary. This algorithm uses its
- ; own stack. If the region is too complex, this stack will overflow.
- ; There is no check for such a stack overflow, but a check can easily be
- ; added.
- ;
- ; Upon entry:
- ;
- ; x-coordinate of interior point is in SI
- ; y-coordinate of interior point is in DI
- ; paint color is in low byte of global variable "color"
- ; boundary color is in high byte of global variable "color"
- ; ES points to video RAM (IBM color display at B800h)
- ; DS points to data segment of SETPT and LOCATE routines
- ;
- ; Coordinate system origin is at top left corner, with y
- ; increasing downward.
- ;
- ; Output:
- ; just to screen
- ;
- ; Registers modified:
- ; none
- ;
- ; Preliminary push and pop procedures
- ;
- ; pushpaint pushes x- and y- coordinates on paint stack
-
- ;=======================================================================
- ; Quadscreen monochrome video buffer stores bits in each scanline from
- ; 0..1023 in 128 consecutive bytes,although only 0..967 are visible.
- ; Successive scan lines are in consecutive groups of 128 bytes, with
- ; scan lines 0..511 filling exactly 64K bytes. A second 64K byte image
- ; immediately follows, but for the moment, we ignore it and assume that
- ; it is not selected.
-
- quadscreen SEGMENT AT 0C000h
- dw 20000 dup (?)
- quadscreen ENDS
-
- ;=======================================================================
-
- stacksize equ 2048d
-
- paintstack SEGMENT
- dw stacksize dup (?)
- paintstack ENDS
-
- ;=======================================================================
-
- $paint SEGMENT PARA PUBLIC 'CODE'
-
- scr_top equ 0d ; top screen line
- scr_bot equ 511d ; bottom screen line
- scr_right equ 1023d ; right screen column (only 0..967 displayable)
- scr_left equ 0d ; left screen column
- y_inc equ (scr_right+1)/8 ; bytes/scanline
-
- ; local constants
-
- bmask db 80h, 40h, 20h, 10h, 08h, 04h, 02h, 01h
-
- ; masks to select right bits in word indexed by offset
-
- ltable dw 0FFFFh, 07FFFh, 03FFFh, 01FFFh
- dw 00FFFh, 007FFh, 003FFh, 001FFh
- dw 000FFh, 0007Fh, 0003Fh, 0001Fh
- dw 0000Fh, 00007h, 00003h, 00001h
-
- ; masks to select left bits in word indexed by offset
-
- rtable dw 08000h, 0C000h, 0E000h, 0F000h
- dw 0F800h, 0FC00h, 0FE00h, 0FF00h
- dw 0FF80h, 0FFC0h, 0FFE0h, 0FFF0h
- dw 0FFF8h, 0FFFCh, 0FFFEh, 0FFFFh
-
-
- ; local variables
-
- color dw ?
- x1 dw ?
- x2 dw ?
- y1 dw ?
- y2 dw ?
- yoff dw ?
- ycount dw ?
- x1woff dw ?
- x2woff dw ?
-
- ;-----------------------------------------------------------------------
- assume cs:$paint,ds:$paint
- assume es:quadscreen
-
- locate PROC NEAR
- ;
- ; Return pixel color at point (X,Y).
- ;
- ; Input:
- ; X in SI
- ; Y in DI
- ; ES points to screen RAM
- ;
- ; Output:
- ; Pixel color (0-1) in AL
- ;
- ; Registers modified:
- ; Only AX
- ;
-
- push bx ; save registers
- push si
-
- mov ax,si ; x
- shr si,1 ; x/2
- shr si,1 ; x/4
- shr si,1 ; (x/8) = byte number
- and ax,07h ; bit offset in byte
- mov bx,di ; y
- shl bx,1 ; y*2
- shl bx,1 ; y*4
- shl bx,1 ; y*8
- shl bx,1 ; y*16
- shl bx,1 ; y*32
- shl bx,1 ; y*64
- shl bx,1 ; y*128
- mov ah,es:[bx+si] ; Get byte from y*128 + (x/8)
- xor bh,bh ; clear top of bx
- mov bh,al ; bit offset into bx
- xor al,al ; al=0
- and ah,bmask[bx] ; mask all but selected bit
- jz locate_done ; return with al=0
- mov al,1 ; return with al=1
-
- locate_done:
- pop si ; restore registers
- pop bx
- ret ; return to caller
-
- locate ENDP
-
- ;-----------------------------------------------------------------------
- assume cs:$paint,ds:$paint
- assume es:quadscreen
-
- fill_partial_box PROC NEAR
- ;
- ; Common routine to fill partial word at same offset in scanline group
- ;
- ; Input:
- ; ax - mask to select bits in word
- ; bx - word offset of word containing starting x in scanline
- ; di - fill color
- ; es - video RAM segment
- ; ycount - scan line count
- ; yoff - starting scan line byte offset in video RAM
- ;
- ; Output:
- ; Just to screen
- ;
- ; Registers modified:
- ; ax,bx,cx,dx,bp,si
- ;
- mov bp,di ; fill color
- and bp,ax ; select bits x1..x2
- not ax ; mask to exclude bits x1..x2
- shl bx,1 ; convert word offset to byte offset
- mov si,yoff ; scanline offset in video RAM
- add si,bx ; si <- yoff+bx for address of first word
- mov cx,ycount ; outer loop count of scan lines
- mov bx,y_inc ; scanline increment
-
- fill_partial_1:
- mov dx,es:[si] ; word from video RAM
- and dx,ax ; clear selected bits
- or dx,bp ; set selected bits
- mov es:[si],dx ; store word back in video RAM
- add si,bx ; point to next scan line
- loop fill_partial_1 ; decrement cx, loop while cx > 0
-
- ret ; return to caller
- fill_partial_box ENDP
-
- ;-----------------------------------------------------------------------
- assume cs:$paint,ds:$paint
- assume es:quadscreen
-
- setbox PROC NEAR
- ;
- ; Fill box with upper left corner at (x1,y1) and lower right corner at
- ; (x2,y2) with value 0..1 in color.
- ;
- ; Input:
- ; Global variables x1,y1,x2,y2,color
- ; ES points to screen RAM
- ;
- ; Output:
- ; Just to screen
- ;
- ; Registers modified:
- ; None
- ;
- ; Note:
- ; Assumes (and exits immediately if not so)
- ; 0 <= x1 <= x2 <= 1023
- ; 0 <= y1 <= y2 <= 511
- ;
- push ax ; save registers
- push bx
- push cx
- push dx
- push bp
- push di
- push si
-
- mov di,0FFFFh ; fill with ones
- test color,di ; test color
- jz setbox_1 ; yes, ones
- xor di,di ; fill with zeros
-
- setbox_1:
- mov ax,x1
- cmp ax,x2 ; x2 >= x1?
- jna setbox_2 ; yes
- jmp setbox_done ; no, exit immediately
-
- setbox_2:
- mov cx,y2
- inc cx ; y2 + 1
- sub cx,y1 ; scan line count = y2 + 1 - y1
- test cx,cx ; count > 0?
- jg setbox_3 ; yes
- jmp setbox_done ; no, exit immediately
-
- setbox_3:
- mov ycount,cx ; save scan line count
- mov si,y1 ; y1
- shl si,1 ; y1*2
- shl si,1 ; y1*4
- shl si,1 ; y1*8
- shl si,1 ; y1*16
- shl si,1 ; y1*32
- shl si,1 ; y1*64
- shl si,1 ; y1*128
- mov yoff,si ; yoff = y1*128
-
- ; The rectangle fill divides into two cases, (a) x1..x2 in one word, and
- ; (b) x1..x2 spanning more than one word. In order to avoid unnecessary
- ; recomputation, the fill in case (b) is split into three loops, first
- ; the left partial word, then the run of complete words, and last, the
- ; right partial word. This gives three tight loops with an absolute
- ; minimum of computation and memory references restricted to the single
- ; fetch and store of the video RAM data, with all other quantities in
- ; registers. If the left and/or right partial words are in fact
- ; complete words, their corresponding loops are skipped and the count
- ; and start of the complete word loop are correspondingly adjusted so as
- ; to include them.
-
- mov bx,x1
- and bx,0FFF0h ; word offset for x1
- mov x1woff,bx ; save word offset
- mov bp,x2
- and bp,0FFF0h ; word offset for x2
- mov x2woff,bp ; save word offset
- cmp bp,bx ; x2 in same word as x1?
- ja setbox_4 ; no
-
- mov bx,x1 ; yes, special case -- x1..x2 in one word
- and bx,0Fh ; bit offset in word
- mov ax,rtable[bx] ; mask to choose bits at right
-
- mov bp,x2
- and bp,0Fh ; bit offset in word
- and ax,ltable[bp] ; combine masks to select bits x1..x2
-
- mov bx,x1woff
- call fill_partial_box
-
- jmp setbox_done ; exit
- ;
- ; General case -- x1..x2 span one or more words, split into three loops
- ;
-
- ;
- ; First loop - set bits in partial word left of scanline
- ;
- setbox_4:
- mov bp,x1
- and bp,0Fh ; bit offset in word
- jz setbox_5 ; skip first loop if fullword
- mov ax,rtable[bp] ; select mask for bits to right
- mov bx,x1woff ; word offset
- call fill_partial_box
- ;
- ; Second loop - set complete words in scanlines
- ;
- setbox_5:
- mov bx,x1woff ; word offset
- mov bp,x2woff ; word offset
- dec bp
- sub bp,bx ; tentative count of complete words to fill
-
- test x1,0Fh ; first word complete?
- jnz setbox_6 ; no
- inc bp ; yes, include it in loop 2
- jmp short setbox_7
-
- setbox_6:
- inc bx ; partial word - adjust offset to 1st fullword
-
- setbox_7:
- mov ax,x2
- and ax,0Fh ; bit offset in last word
- cmp ax,0Fh ; last word complete?
- jne setbox_8 ; no
- inc bp ; yes, include it in loop 2
-
- setbox_8:
- test bp,bp ; count >= 0?
- jna setbox_10 ; skip loop if 2 adjacent partial words
-
- mov ax,di ; fill color
- shl bx,1 ; convert word offset to byte offset
- mov si,yoff ; scanline offset in video RAM
- add si,bx ; si <- yoff+bx for address of first word
- mov cx,ycount ; outer loop count of scan lines
- mov bx,y_inc ; scanline increment
- cld ; clear direction flag for increment of di
-
- ; Loop 2 -- run of full words
-
- setbox_9:
- mov di,si ; address of first full words
- mov dx,cx ; save outer loop count
- mov cx,bp ; inner loop count
- rep stosw ; loop filling ax into [es:di]
- mov cx,dx ; restore output loop count
- add si,bx ; point to next scan line
- loop setbox_9 ; decr cx, loop while cx > 0
-
- mov di,ax ; restore fill color
- ;
- ; Set bits in partial word right of scanline
- ;
- setbox_10:
- mov bp,x2
- and bp,0Fh ; bit offset
- cmp bp,0Fh ; fullword?
- je setbox_done ; yes, already filled by loop 2
- mov ax,ltable[bp] ; mask to select bits 0..x2
- mov bx,x2woff ; word offset
- call fill_partial_box
-
- setbox_done: ; restore registers
- pop si
- pop di
- pop bp
- pop dx
- pop cx
- pop bx
- pop ax
- ret ; return to caller
-
- setbox ENDP
-
- ;-----------------------------------------------------------------------
- assume cs:$paint,ds:$paint
-
- pushpaint PROC NEAR
- dec bp ; bp is paint stack pointer
- dec bp ; and gets decremented first
- mov [bp],si ; push x
- dec bp
- dec bp
- mov [bp],di ; push y
- ret
- pushpaint ENDP
-
- ;-----------------------------------------------------------------------
- assume cs:$paint,ds:$paint
-
- poppaint PROC NEAR
- ;
- ; poppaint pops x- and y-coordinates off paint stack
- ;
- mov di,[bp] ; pop y
- inc bp ; increment paint stack pointer
- inc bp
- mov di,[bp] ; pop x
- inc bp ; increment paint stack pointer
- inc bp
- ret
- poppaint ENDP
-
- ;-----------------------------------------------------------------------
-
-
- argcnt equ 3
- x equ [bp]
- y equ [bp-2]
- colval equ [bp-4]
- ; return address as CS:offset at [BP-6..9]
- ; old BP at [BP-10..11]
- spbias equ 10 ; new sp points at last local stack variable
-
-
- assume cs:$paint,ds:$paint
-
- paint PROC FAR
-
- push bp ; Save caller's frame pointer
- mov bp,sp ; and set up our own.
- add bp,4+2*argcnt ; Point to top of arg list on stack
- sub sp,spbias ; Reserve space for locals on stack
-
- push ds
- mov ax,cs
- mov ds,ax ; establish ds=cs addressability
-
- mov si,x
- mov di,y
- mov ax,colval
- mov color,ax
- mov dx,color ; initialize paint color
- ;
- ; bp no longer used as variable pointer
- ;
- mov ax,quadscreen
- mov es,ax ; establish quadscreen addressability
- ;
- ; initialize paint stack
- ;
- mov bp,paintstack ; bp is set to top of stack
- call pushpaint ; push interior point onto stack
- ;
- ; main loop for painting
- ;
- paint1:
- mov ax,paintstack ; stack empty?
- cmp bp,ax
- jne paint2 ; continue if not
- jmp endpaint ; else exit
-
- ;
- ; get the next place to paint
- ;
- paint2:
- call poppaint ; pop the next place to paint
- call locate ; color is returned in al
- cmp al,dl ; is it filled?
- je paint1 ; yes
- cmp al,dh ; is it boundary?
- je paint1 ; yes
- cmp di,scr_top ; top of screen?
- jl paint1 ; no
- cmp di,scr_bot ; bottom of screen?
- jg paint1 ; yes
- ;
- ; move right until boundary reached
- ;
- paint3:
- inc si ; x <-- x + 1
- call locate ; look right
- dec si ; restore x
-
- cmp al,dl ; is it filled?
- je paint4 ; yes
- cmp al,dh ; is it boundary?
- je paint4 ; yes
- cmp si,scr_right ; at right screen boundary?
- je paint4 ; yes
- inc si ; x <-- x + 1
- jmp paint3
- ;
- ; push above and below
- ;
- paint4:
- dec di ; y <-- y - 1
- call locate ; check above
- mov bh,al ; save above state
- cmp al,dl ; is it filled?
- je paint5 ; yes
- cmp al,dh ; is it boundary color?
- je paint5 ; yes
- call pushpaint ; no, push above
-
- paint5:
- inc di ; restore y
- inc di ; y <-- y + 1
- call locate ; check below
- mov bl,al ; save below state
- cmp al,dl ; is it filled?
- je paint6 ; yes
- cmp al,dh ; is it boundary color
- je paint6
- call pushpaint ; push below
-
- paint6:
- dec di ; restore y
- ;
- ; anchor the end point of the scan line
- ;
- mov x2,si ; store x-coordinate of end of scan line
- mov y2,di ; store y-coordinate of end of scan line
- ;
- ; plot as we scan left, checking above and below
- ;
- paint7:
- dec di ; y <-- y - 1
- call locate ; check above
- cmp al,dl ; is it filled?
- je paint9
- cmp al,dh ; is it boundary color?
- je paint9
-
- cmp bh,dl ; last above filled?
- je paint8 ; yes
- cmp bh,dh ; boundary color?
- jne paint9 ; no
-
- paint8:
- call pushpaint ; push above if new place to paint
-
- paint9:
- mov bh,al ; update last above
- inc di ; restore y
- inc di ; y <-- y + 1
- call locate ; check below
- cmp al,dl ; is it filled?
- je paint11 ; yes
- cmp al,dh ; is it boundary color?
- je paint11 ; yes
-
- cmp bl,dl ; last below filled?
- je paint10 ; yes
- cmp bl,dh ; was it boundary color?
- jne paint11 ; no
-
- paint10:
- call pushpaint ; push below if new place to paint
- paint11:
- dec di ; restore y
- mov bl,al ; update last below
-
- dec si ; move left, x <-- x - 1
- jl paint12 ; stop the scan if too far left
- call locate ; check the point
- cmp al,dl ; hit filled yet?
- je paint12 ; yes, next scan line
- cmp al,dh ; hit boundary yet?
- jne paint7 ; no
-
- paint12: ; yes, continue next scan line
- inc si ; restore x
- mov x1,si ; store x-coordinate of start
- mov y1,di ; store y-coordinate of start
- call setbox ; plot the scan line
- jmp paint1 ; next place to paint
-
- endpaint:
- pop ds ; restore ds
- add sp,spbias ; restore original sp at entry
- pop bp ; and callers frame pointer
- ret 2*argcnt ; and return discarding stack arguments
-
- paint ENDP
-
- ;-----------------------------------------------------------------------
-
- $paint ENDS
- END